package com.haifeng.wdsh.auth.config;

import com.haifeng.wdsh.auth.exception.AuthWebResponseExceptionTranslator;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;

/**
 * <p>
 *  认证服务器配置中心
 * </p>
 *
 * @author: Haifeng
 * @date: 2020-05-06
 */
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class AuthAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final DataSource dataSource;
    private final TokenStore tokenStore;
    private final PasswordEncoder passwordEncoder;
    private final JwtAccessTokenConverter jwtAccessTokenConverter;
    private final ClientDetailsService clientDetailsService;
    private final AuthenticationManager authenticationManagerBean;
    private final AuthWebResponseExceptionTranslator authWebResponseExceptionTranslator;

    /**
     * 1、配置客户端信息,JDBC方式，具体查询oauth_client_details
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService(dataSource));
    }

    @Bean("mysqlAuthClientDetails")
    public ClientDetailsService clientDetailsService(DataSource dataSource){
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    /**
     * 2、配置令牌访问断点
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /*
        * authenticationManager：用户名密码模式必须配置
        * authorizationCodeServices：授权码模式必须配置
        */
        endpoints.authenticationManager(authenticationManagerBean)
                 .tokenServices(authorizationServerTokenServices())
                 .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST)
                 .exceptionTranslator(authWebResponseExceptionTranslator);
    }

    /**
     * 安全策略
     * allowFormAuthenticationForClients：允许表单认证
     * checkTokenAccess：允许任何人检查令牌
     * tokenKeyAccess：JWT公钥使用
     * @param security
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    /**
     * 令牌管理服务
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices authorizationServerTokenServices(){
        DefaultTokenServices tokenService = new DefaultTokenServices();
        // 配置客户端信息
        tokenService.setClientDetailsService(clientDetailsService);
        // 配置令牌存储方式
        tokenService.setTokenStore(tokenStore);
        // 默认令牌有效期24小时
        tokenService.setAccessTokenValiditySeconds(86400);
        // 默认刷新令牌有效期7天
        tokenService.setRefreshTokenValiditySeconds(604800);
        // 是否支持刷新令牌
        tokenService.setSupportRefreshToken(true);
        // 是否可以重复使用刷新令牌
        tokenService.setReuseRefreshToken(false);
        // 配置JWT
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        tokenService.setTokenEnhancer(tokenEnhancerChain);
        return tokenService;
    }
}
